/* init.c
 * By: Ariq Chowdhury
 * Date created: March 17th 2011
 * Date modified: March 17th 2011
 */

#include <init.h>

typedef void (*vec_addr)(void);

/* These functions are defined in int_handler.S */
vec_addr interrupt_vec[3] = {hwint00, hwint01, swint00};
vec_addr exception_vec[NUM_EXCEPTIONS] = 
{ 
 exc_div_by_zero,
 exc_debug,
 exc_non_maskable_interrupt,
 exc_breakpoint,
 exc_overflow,
 exc_bound_range_exceeded,
 exc_invalid_opcode,
 exc_device_not_avail,
 exc_double_fault,
 exc_coprocesssor_segment_overrun,
 exc_invalid_tss,
 exc_seg_not_present,
 exc_stack_seg_fault,
 exc_gen_protection_fault,
 exc_page_fault,
 exc_reserved,
 exc_x87fp,
 exc_alignment_check,
 exc_machine_check,
 exc_simd_fp,
 exc_reserved,
 exc_reserved,
 exc_reserved,
 exc_reserved,
 exc_reserved,
 exc_reserved,
 exc_reserved,
 exc_reserved,
 exc_reserved,
 exc_reserved,
 exc_security,
 exc_reserved,
};

void idt_install()
{
  idtp.limit = (sizeof(struct idt_entry) * IDT_ENTRIES) - 1;
  idtp.base = &idt;

  idt_set_gate();

  idt_load();
}

/* Offset is the address of the function to be run
 * Selector is pointing to the first entry in the GDT-the Ring0 CodeSegment
 * Zero should be obvious
 * flags: 0x8 = Interrupt in use and DPL->0  0xE = Interrupt Gate
 */
void idt_set_gate()
{
  set_exceptions();

  idt[CLK_IDT_INDEX].offset_l = ((uint32_t)interrupt_vec[0] & 0xffff);
  idt[CLK_IDT_INDEX].selector = 0x0008;
  idt[CLK_IDT_INDEX].zero = 0;
  idt[CLK_IDT_INDEX].flags = 0x8E;
  idt[CLK_IDT_INDEX].offset_h = ((uint32_t)interrupt_vec[0] & 0xffff0000) >> 16;
  
  idt[KEYBOARD_IDT_INDEX].offset_l = ((uint32_t)interrupt_vec[1] & 0xffff);
  idt[KEYBOARD_IDT_INDEX].selector = 0x0008;
  idt[KEYBOARD_IDT_INDEX].zero = 0;
  idt[KEYBOARD_IDT_INDEX].flags = 0x8E;
  idt[KEYBOARD_IDT_INDEX].offset_h = ((uint32_t)interrupt_vec[1] & 0xffff0000) >> 16;

  idt[SYSCALL_IDT_INDEX].offset_l = ((uint32_t)interrupt_vec[2] & 0xffff);
  idt[SYSCALL_IDT_INDEX].selector = 0x0008;
  idt[SYSCALL_IDT_INDEX].zero = 0;
  idt[SYSCALL_IDT_INDEX].flags = 0x8E;
  idt[SYSCALL_IDT_INDEX].offset_h = ((uint32_t)interrupt_vec[2] & 0xffff0000) >> 16;
}

/* 
 * Exception handlers must start from 0x0 and go to 0x20
 */
void set_exceptions()
{
  int i;
  
  /* For loop is temporary until we have separate exception handlers for
   * each exception; for now, they all call the same handler which kernel panics
   */
  for(i = 0; i < NUM_EXCEPTIONS; i++) {
    idt[i].offset_l = ((uint32_t)exception_vec[i] & 0xffff);
    idt[i].selector = 0x0008;
    idt[i].zero = 0;
    idt[i].flags = 0x8E;
    idt[i].offset_h = ((uint32_t)exception_vec[i] & 0xffff0000) >> 16;
  }
}
